import org.codehaus.groovy.grails.commons.ApplicationHolder
import org.codehaus.groovy.grails.commons.GrailsClassUtils
import org.codehaus.groovy.grails.commons.ConfigurationHolder

/**
* The purpose of this security filter is to intercept calls to the application
* and authenticate and authorize the request.
*/
class SecurityFilters {

    AuthService authService

    def filters = {
        log.warn "Configuring filters!"
        // Check whether the action is protected for authentication
        authenticationCheck(controller: '*', action: '*') {
            before = {
                def anActionName = actionName;
                if (!anActionName) {
                    anActionName = "index"
                }
//                println "Into before() with ${controllerName}:${anActionName}"
                def application = ApplicationHolder.application;
                def classes = application.controllerClasses
                Class ctrlCls = null;
                for (Class cls in classes) {
                    // Get its short name
                    def ctrlName = cls.logicalPropertyName
                    if (ctrlName.equals(controllerName)) {
                        ctrlCls = cls;
                        break;
                    }
                }
                if (!ctrlCls) {
//                    println "Didn't find ${controllerName}"
                    return true;
                }

                // Now we check whether the controller action requires authentication
                boolean actionProtected = false;
                Object authenticate = GrailsClassUtils.getStaticPropertyValue(ctrlCls.getClazz(), "authenticate");
                if (authenticate instanceof Boolean) {
                    // All actions are authenticated so authenticate
                    actionProtected = true;                    
                } else if (authenticate instanceof Map) {
                    def authMap = authenticate as Map
                    List except = authMap['except']
                    List only = authMap['only'];

                    if (except) {
                        if (anActionName in except) {
                            actionProtected = false;
                        } else {
                            actionProtected = true;
                        }
                    } else if (only) {
                        if (anActionName in only) {
                            actionProtected = true;
                        } else {
                            actionProtected = false;
                        }
                    }
                } else {
//                    println "Authenticate was unrecognized class ${authenticate.getClass().getName()}"
                    return true; // we ignore anything else
                }
                
//                println "${controllerName}:${anActionName} protected: ${actionProtected}"
                if (actionProtected) {
                    if (!session.authenticated) {
                        def originalRequestParams = [controller: controllerName, action: anActionName]
                        originalRequestParams.putAll(params)
                        session.originalRequestParams = originalRequestParams
//                        flash.message = "login.prompt"
                        def appConfig = ConfigurationHolder.config
                        def loginCtrl = appConfig.authorize.login.controller
                        def loginAction = appConfig.authorize.login.action
//                        println "Redirecting request to login page ${loginCtrl}:${loginAction}"
                        session.authenticate = "true"
                        if (loginAction) {
                            redirect(controller: loginCtrl, action: loginAction)
                        } else {
                            redirect(controller: loginCtrl)
                        }
                        return false
                    }
                }                
            }
        }

        // Check whether the action requires authorization
        authorizationCheck(controller: '*', action: '*') {
            before = {
                def aControllerName = controllerName;
                def anActionName = actionName;
                if (!anActionName) {
                    anActionName = "index"
//                    println "Action name set to index is now ${anActionName}"
                }
                
                // See if the controller action requires authorization
                boolean userAuthorized = true;
                AuthControler ctrl = AuthControler.findByName(aControllerName)
                if (ctrl) {

                    // Find the associated Action
//                    println "[Authorize] anActionName is ${anActionName}"
                    AuthAction action = AuthAction.findByControlerAndName(ctrl, anActionName);
                    if (action) {
                        if (authService) {
//                            println "The service is not null"
                        }
                        if (action.roles.size() == 0) {// which would be very odd
//                            println "[Authorize] Controller ${controllerName} mapped to action ${anActionName} with no roles"
                        } else {
                            if (!session.authenticated) {
                                userAuthorized = false;
                            } else {
                                // Even though we have an AuthUser object in the session, we need to reload it because
                                // it is not associated with a session.
                                AuthUser user = AuthUser.get(session.authenticated.id)
                                if (!user) {
                                    userAuthorized = false; // TODO: something is amiss - every user should have an AuthUser
                                } else {
                                    userAuthorized = false;
                                    for (AuthRole aRole in action.roles) {
                                        if (user.hasRole(aRole.name)) {
                                            userAuthorized = true;
                                            break;
                                        }
                                    }                                    
                                }
                            }                            
                        }
                    }
                }
                
//                println "${controllerName}:${anActionName} authorized: ${userAuthorized}"
                if (!userAuthorized) {
//                    println("Unauthorized access to ${controllerName}:${anActionName}")
                    def originalRequestParams = [controller: controllerName, action: anActionName]
                    originalRequestParams.putAll(params)
                    session.originalRequestParams = originalRequestParams
                    session.unauthorized = "true"
                    redirect(controller: 'login')
                    return false
                }
//                println "Returning true from before()"
                return true;
            }

        }
    }

}